package main

import (
	"fmt"
	"math"
)

type Peaple struct {
	Name string
	Age  int
}
type MyFloat float64

func (p Peaple) Say() {
	fmt.Println("My name is ", p.Name, ". I ", p.Age, " years old")
}

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f) // 强制类型转换
	}
	return float64(f)
}

type Vertex struct {
	X, Y float64
}

// 普通的对照测试
func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// 当是指针来当作接受者声明方法时的实例
func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f //等价于(*v).X * f
}
func main() {
	p1 := Peaple{
		Name: "AEA",
		Age:  16,
	}
	p1.Say()
	f := MyFloat(-1.23456) // 理解这个是什么赋值，可以理解为将 -1.23456 强制转换成MyFloat类型，这个类型有我们自己定义的方法
	fmt.Println(f.Abs())
	in := 10
	fn := int(20)
	fmt.Println(in)
	fmt.Println(fn)
	v := Vertex{3, 4}
	fmt.Println(v.Abs())
	v.Scale(10)
	fmt.Println(v.Abs())
	p := &v
	p.Scale(0.1)
	fmt.Println(p.Abs())
	/*
		比较前两个程序，你大概会注意到带指针参数的函数必须接受一个指针：
		var v Vertex
		ScaleFunc(v, 5)  // 编译错误！
		ScaleFunc(&v, 5) // OK
		而以指针为接收者的方法被调用时，接收者既能为值又能为指针：
		var v Vertex
		v.Scale(5)  // OK
		对于语句 v.Scale(5)，即便 v 是个值而非指针，带指针接收者的方法也能被直接调用。
		也就是说，由于 Scale 方法有一个指针接收者，为方便起见，Go 会将语句 v.Scale(5) 解释为 (&v).Scale(5)。
		但是在接口里面又是另外一种情况
		p := &v
		p.Scale(10) // OK

		同样的事情也发生在相反的方向。
		接受一个值作为参数的函数必须接受一个指定类型的值：
		var v Vertex
		fmt.Println(AbsFunc(v))  // OK
		fmt.Println(AbsFunc(&v)) // 编译错误！
		而以值为接收者的方法被调用时，接收者既能为值又能为指针：
		var v Vertex
		fmt.Println(v.Abs()) // OK
		p := &v
		fmt.Println(p.Abs()) // OK
	*/
}

/*
理解方法:其实就是类似于其他语言中的类和实例，首先定一个结构体类型，这个就类似于一个类，然后为这个类绑定一些函数，这些函数就称为这个结构体的方法。
当需要使用时；先声明一个结构体变量(类似于实例化一个对象)。然后可以执行这个结构体的方法
*/
